#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
#include <functional>
#include "Socket.hpp"

using namespace SocketMoudle;
// 实现一个自定义的网络版本的计算器
// 协议---结构化数据

// 实现序列化和反序列化：1. 自主实现 2. 现成方案json--->jsoncpp
// 完整报文：content_len\r\n{"x": 10, "y": 20, "oper": '+'}\r\n
// => 应用层报头: content_len报文的长度字段
// => [\r\n]用来区分报头和有效载荷
// client -> server
class Request
{
public:
    Request()
    {
    }
    Request(int x, int y, char oper)
        : _x(x),
          _y(y),
          _oper(oper)
    {
    }
    // 序列化
    std::string Serialize()
    {
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = (int)_oper; // ASSIC码值

        Json::FastWriter writer;
        std::string s = writer.write(root);
        return s;
    }
    // 反序列化
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        if (ok)
        {
            _x = root["x"].asInt();
            _y = root["y"].asInt();
            _oper = (char)root["oper"].asInt();
        }
        return ok;
    }
    int X() const { return _x; }
    int Y() const { return _y; }
    char Oper() const { return _oper; }

    ~Request()
    {
    }

private:
    int _x;
    int _y;
    char _oper; // 算数符号 eg: _x oper _y
};

// server -> client
class Response
{
public:
    Response() {}
    Response(int result, int code)
        : _result(result),
          _code(code)
    {
    }

    // 序列化
    std::string Serialize()
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        return writer.write(root);
    }
    // 反序列化
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        if (ok)
        {
            _result = root["result"].asInt();
            _code = root["code"].asInt();
        }
        return ok;
    }

    void SetResult(int res)
    {
        _result = res;
    }
    void SetCode(int code)
    {
        _code = code;
    }

    void ShowResult()
    {
        std::cout << "结果是：" << _result << "[" << _code << "]" << std::endl;
        ;
    }

    ~Response() {}

private:
    int _result; // 运算结果
    int _code;   // 运算状态值, 0表示success; 1.2.3.4表示不同的运算异常情况--->约定(协议)
};

// 实现业务
using func_t = std::function<Response(const Request &req)>;

const std::string sep = "\r\n"; // 分隔符
// 协议(基于TCP)需要解决两个问题
// 1. request 和 response 必须都有序列化和反序列化的功能
// 2. 读取时要读到完整的请求(UDP不考虑->面向数据报)
class Protocol
{
public:
    Protocol()
    {
    }
    Protocol(func_t func) : _func(func)
    {
    }
    // 封装
    std::string Encode(const std::string jsonstr)
    {
        // content_len\r\n{"x": 10, "y": 20, "oper": '+'}\r\n
        std::string len = std::to_string(jsonstr.size()); // 报文长度
        return len + sep + jsonstr + sep;                 // 应用层封装报头
    }

    // 解包
    // 1. 判断报文完整性
    // 2. 如果至少包含一个完整请求，就得提取出来，并从缓冲区移除它，方便处理下一个
    bool Decode(std::string &buffer, std::string *package)
    {
        ssize_t pos = buffer.find(sep);
        if (pos == std::string::npos)
            return false;                                    // 让调用方继续从内核中读取
        std::string package_len_str = buffer.substr(0, pos); // 提取出长度字段
        int package_len_int = std::stoi(package_len_str);

        // buffer一定有长度，但不一定有完整报文
        int target_len = package_len_str.size() + package_len_int + 2 * sep.size(); // 完整报文的长度
        if (buffer.size() < target_len)
            return false; // 报文不完整
        // 此时, buffer中一定至少有一个完整的报文
        *package = buffer.substr(pos + sep.size(), package_len_int);
        buffer.erase(0, target_len); // 移除整个报文
        return true;
    }

    // 通过协议在网络中读取数据
    void GetRequest(std::shared_ptr<Socket> &sock, InetAddr &client)
    {
        // 读取
        std::string buffer_queue; // 相当于缓冲区队列
        while (true)              // 长服务
        {
            int n = sock->Recv(&buffer_queue);
            if (n > 0)
            {
                std::cout << "-------------request_buffer---------------" << std::endl;
                std::cout << buffer_queue << std::endl;
                std::cout << "------------------------------------------" << std::endl;

                // 1. 解析报文，提取完整的json请求，如果不完整就让服务器重新读取
                std::string json_package;
                while (Decode(buffer_queue, &json_package))
                {
                    // if (!ret)
                    //     continue; // 没有完整报文，就继续从缓冲区读取

                    // 此时已经拿到了一个完整的报文：{"x": 10, "y": 20, "oper": '+'}
                    // 2. 请求json串，要进行反序列化
                    std::cout << "---------------request_json---------------" << std::endl;
                    std::cout << json_package << std::endl;
                    std::cout << "------------------------------------------" << std::endl;

                    LOG(LogLevel::DEBUG) << client.StringAddr() << " 请求：" << json_package;
                    Request req;
                    bool ok = req.Deserialize(json_package);
                    if (!ok)
                        continue;

                    // 3. 一定得到了一个内部属性已经被设置了的 req (结构化信息)
                    // 通过req->resp, 实现计算功能(业务)！
                    Response resp = _func(req);

                    // 4. 对计算结果序列化
                    std::string json_str = resp.Serialize();

                    // 5. 封装报文：添加自定义长度(遵循协议规定)
                    std::string send_str = Encode(json_str); // 携带长度的应答报文

                    // 6. 发送
                    sock->Send(send_str);
                }
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client: " << client.StringAddr() << " Quit"; // 客户端退出了
                break;
            }
            else
            {
                LOG(LogLevel::WARNING) << "client: " << client.StringAddr() << " recv error"; // 读取失败了
                break;
            }
            // std::cout << "inbuffer:\n" << inbuffer << std::endl;
        }
        sock->Close();
    }

    bool GetResponse(std::shared_ptr<Socket> &client, std::string &resp_buffer, Response *resp)
    {
        // 面向字节流，怎么保证读到的网络字符串是一个完整的请求
        while (true)
        {
            int n = client->Recv(&resp_buffer);
            if (n > 0)
            {
                // std::cout << "--------------resp_buffer-----------------" << std::endl;
                // std::cout << resp_buffer << std::endl;
                // std::cout << "------------------------------------------" << std::endl;

                // 成功
                // 1. 解析报文，提取完整的json请求，如果不完整就让服务器重新读取
                std::string json_package;

                // 备份
                // bool ret = Decode(resp_buffer, &json_package);
                // if (!ret)
                //     continue; // 没有完整报文，就继续从缓冲区读取

                while (Decode(resp_buffer, &json_package))
                {
                    // std::cout << "--------------response json---------------" << std::endl;
                    // std::cout << json_package << std::endl;
                    // std::cout << "------------------------------------------" << std::endl;

                    // std::cout << "--------------resp_buffer-----------------" << std::endl;
                    // std::cout << resp_buffer << std::endl;
                    // std::cout << "------------------------------------------" << std::endl;

                    // 此时一定拿到了一个完整的json报文
                    // 2. 反序列化
                    resp->Deserialize(json_package);
                    resp->ShowResult();
                }
                return true;
            }
            else if (n == 0)
            {
                std::cout << "server quit" << std::endl;
                return false;
            }
            else
            {
                std::cout << "recv error" << std::endl;
                return false;
            }
        }
    }

    // 构建请求字符串
    std::string BuildRequestString(int x, int y, char oper)
    {
        // 1. 构建一个完整的请求
        Request req(x, y, oper);

        // 2. 序列化
        std::string json_req = req.Serialize();

        // 2.1 debug
        // std::cout << "-------------json_req_string--------------" << std::endl;
        // std::cout << json_req << std::endl;
        // std::cout << "------------------------------------------" << std::endl;

        // 3. 添加长度报头
        return Encode(json_req);
    }

    ~Protocol()
    {
    }

private:
    // 用的是多进程，会对临时数据进行拷贝
    // Request _req;
    // Response _resp;
    func_t _func;
};
